# -*- coding: utf-8 -*-
# vim: set noet ts=4:
#
# scim-python
#
# Copyright (c) 2007-2009 Yu Fan <yufanyufan@gmail.com>
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA
#
# $Id: $
#
import os
import traceback
from sets import Set
import popen2
from gettext import dgettext

from ibus import keysyms
from ibus import modifier
from ibus import ascii
import ibus

from npcore import *

_ = lambda a : dgettext ("ibus-natural-pinyin", a)
RGB = lambda r, g, b : (((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))



class InputException(Exception):
	def __init__ (self):
		Exception.__init__(self)


class Engine (ibus.EngineBase):
	def __init__ (self, conn, object_path):
		super(Engine, self).__init__(conn, object_path)
		
		self._editor = Editor ()
		self._lookup_table = ibus.LookupTable (9)
		self._status_property = ibus.Property ("chinese")
		self._status_property.label = "CN"
		self._setup_property = ibus.Property ("setup")
		self._setup_property.label = "Setup"
		self._prop_list = ibus.PropList()
		self._prop_list.append(self._status_property)
		self._prop_list.append(self._setup_property)
		#setup_property.icon = "/usr/share/scim/icons/setup.png"
		self._chinese_mode = True
#		self._log = scim.Log.Log ("ZhengJu")

		#~ print "init"
	def clear(self):
		pass
	def reset(self):
		#~ print "reset"
		if self._editor.wordlist:
			self.commit_text (ibus.Text(self._editor.commit()))
		else:
			self._editor.clear()
		self.clear()
		self.origin_string = None
		self._double_quotation_state = False
		self._single_quotation_state = False
		self._prev_key = None
		self._shift_key = None	
		self.pipe = None	
		self.update ()	
		self.register_properties (self._prop_list)
		self.update_properties ()

	def update_preedit (self):
		string = self._editor.get_preedit () + self.get_extra_string()
		if (string == u""):
			self.hide_preedit_text ()
		else:
			self.show_preedit_text ()
			attrs = ibus.AttrList()
			attrs.append(ibus.AttributeForeground(ibus.RGB(0,0,255), 0, len(string)))
			attrs.append(ibus.AttributeUnderline(ibus.ATTR_UNDERLINE_SINGLE, 0, len(string)))
			self.update_preedit_text (ibus.Text(string, attrs) , self._editor.get_screen_cursor(), True)

	def focus_out(self):
		#~ print "out reset"
		self.reset()

#	def set_cursor_location(self, x, y, w, h):
#		self.reset()

	def focus_in (self):
		#~ print "in reset"
		self.reset()
	
	def property_activate(self, prop_name, prop_state = ibus.PROP_STATE_UNCHECKED):
		if prop_name == "chinese":
			self.change_mode ()
		elif prop_name == "setup":
			setup_cmd = path.join(os.getenv('LIBEXECDIR'), "ibus-setup-natural-pinyin")
			Engine.__setup_pid = os.spawnl(os.P_NOWAIT, setup_cmd, "ibus-setup-natural-pinyin")

	def update_candidate (self):
		if self._editor.candidates:
			self.update_lookup_table(self._editor.lookup_table, True)
			self.show_lookup_table()
		else:
			self.hide_lookup_table ()

	def update_aux(self):
		if self._editor.predict:
			self.show_auxiliary_text ()
			string = self._editor.get_aux ()
			#attrs = [scim.Attribute (0, len (string), scim.ATTR_FOREGROUND, RGB (0, 0, 0xef))]
			self.update_auxiliary_text (ibus.Text(string), True)
		else:
			self.hide_auxiliary_text ()

	def update (self):
		self.update_preedit ()
		self.update_aux ()
		self.update_candidate ()
		
	def candidate_clicked(self, index, button, state):
		if button == 1:
			self.process_key_event(keysyms._1 + index, 0, state)
			
	def update_properties (self):
		if self._chinese_mode: # refresh mode
			self._status_property.label = _("CN")
		else:
			self._status_property.label = _("EN")
		self.update_property(self._status_property)

	def change_mode(self):
		if self._chinese_mode:
			self.commit_text (ibus.Text(self._editor.commit()))
			self.update()
		self._chinese_mode = not self._chinese_mode
		self.update_properties ()
		#~ print "change_mode", self._chinese_mode

	@classmethod
	def CONFIG_RELOADED(cls, config):
		Engine.progresivepromp = config.get_value("engine/NaturalPinYin", "ProgressivePrompt", False)

	def lookup_table_page_down (self):
		self._editor.lookup_table.page_down ();
		self.update()
		return True

	def lookup_table_page_up (self):
		self._editor.lookup_table.page_up ();
		self.update()
		return True
		
	def process_key_event (self, keyval, keycode, state):
		#print keyval, keycode, state, self._chinese_mode
		if self._chinese_mode and self._editor.is_end() and not self.get_extra_string():
			if (keyval == ibus.keysyms.Shift_L or keyval == ibus.keysyms.Shift_R)	\
				and state & ibus.modifier.RELEASE_MASK:
				if self._shift_key:
					self.change_mode()
					return True
			if state == ibus.modifier.SHIFT_MASK and (keyval >= ibus.keysyms.A and keyval <= ibus.keysyms.Z) \
				or state & ibus.modifier.LOCK_MASK:
				self.change_mode()
		elif not self.get_extra_string():
			if (keyval == ibus.keysyms.Shift_L or keyval == ibus.keysyms.Shift_R)\
				and state & ibus.modifier.RELEASE_MASK:
				if self._shift_key:
					self.change_mode()
					return True
		if (keyval == ibus.keysyms.Shift_L or keyval == ibus.keysyms.Shift_R)	\
			and state == 0:
			self._shift_key = True;
		else:
			self._shift_key = None;
		if self.pipe and self.pipe.poll() != -1:
			try: 
				self.origin_string = unicode(self.pipe.fromchild.read()[:-1],"utf8")
				self._editor.reverse(self.origin_string)
				self._editor.move_cursor_to (1)
			except:
				self._editor.clear()
				#self.beep ()
			else:
				self.commit_text(ibus.Text(u""))
				self.update()			
			finally:
				self.pipe = None
				return True
		if state & ibus.modifier.RELEASE_MASK:
			return False
		try:
			if self._chinese_mode:
				result = self.chinese_process_key_event (keyval, keycode, state)
			else:
				result = self.english_process_key_event (keyval, keycode, state)
			self._prev_key = keyval
		except InputException, e:
			#self.beep ()
			return True
		except Exception, e:
			traceback.print_exc();
			#self.beep ()
			#sself._log.print_exc()
			#sself._log.log("DEBUG", self._editor.cursor)
			#sself._log.log("DEBUG", [i.char.encode("utf-8") for i in self._editor.wordlist] )
			#sself._log.log("DEBUG", [i.get_screen_pinyin() for i in self._editor.pinyinlist] )
			#sself._log.log("DEBUG", self._editor.get_preedit().encode ("utf-8"))
			return True
		else:
			return result	
		finally:
			self.update()

	def english_process_key_event (self, keyval, keycode, state):
		return False

	def chinese_process_key_event (self, keyval, keycode, state):
		#print "chinese"
		if self._editor.is_empty() and not self.get_extra_string():
			if keyval <= 127 and ascii.ispunct (chr (keyval)):
				#print "ispunct"
				self.commit_text (ibus.Text(self.convert_to_full_width (unichr (keyval))))
				return True
			elif keyval == ibus.keysyms.r and state == ibus.modifier.CONTROL_MASK:
				if not self.pipe:
					self.pipe = popen2.Popen3("python -c" +'"import gtk; print gtk.clipboard_get(selection=\\"PRIMARY\\").wait_for_text()"')
				return True
			else:
				return False
		#print unichr (keyval)
		if keyval in (ibus.keysyms.Control_L,ibus.keysyms.Control_R,
			ibus.keysyms.Alt_L, ibus.keysyms.Alt_R):
			return True
		elif keyval in (ibus.keysyms.KP_Space, ibus.keysyms.space):
			#print self._editor.get_candidate_cursor()
			if self._editor.candidates and self._editor.lookup_table.is_cursor_visible():
				self._editor.select_cursor()
				return True
			elif self._editor.pinyinlist:
				if self._editor.wordlist:
					self._editor.convert_all ()
				else:
					self._editor.convert_all ()
					self.commit_text (ibus.Text(self._editor.commit()))
				return True
			elif self._editor.cursor < len (self._editor.wordlist):
				self._editor.jump_to_next_word()
				return True
			else:
				self.commit_text (ibus.Text(self._editor.commit()))
				return True
		elif keyval == ibus.keysyms.BackSpace:
			if not self._editor.pinyinlist and self.get_extra_string():
				raise InputException()
			self._editor.del_current()
			return True
		elif keyval == ibus.keysyms.Delete:
			if self._editor.lookup_table.is_cursor_visible():
				self._editor.delete_cursor_phrase ()
			else:
				self._editor.del_next ()
			return True
		elif keyval >= ibus.keysyms._0 and keyval <= ibus.keysyms._9 and state & ibus.modifier.CONTROL_MASK:
			self._editor.delete_phrase (keyval - ibus.keysyms._1)
			return True
		elif keyval >= ibus.keysyms._0 and keyval <= ibus.keysyms._9 and state & ibus.modifier.ALT_MASK:
			self._editor.move_cursor_to (keyval - ibus.keysyms._0)
			return True
		elif keyval >= ibus.keysyms._1 and keyval <= ibus.keysyms._9:
			self._editor.select (keyval-ibus.keysyms._1)
			return True
		elif keyval >= ibus.keysyms.KP_1 and keyval <= ibus.keysyms.KP_9:
			self._editor.select (keyval-ibus.keysyms.KP_1)
			return True
		elif keyval == ibus.keysyms.Shift_L:
			if not self._editor.is_end():
				self._editor.select (0)
				self._shift_key = None
			return True
		elif keyval == ibus.keysyms.Shift_R:
			if not self._editor.is_end():
				self._editor.select (1)
				self._shift_key = None
			return True
		elif keyval in (ibus.keysyms.equal, ibus.keysyms.bracketright, ibus.keysyms.Page_Down):
			if self._editor.candidates:
				self._editor.lookup_table.page_down ();
				return True
			else:
				raise InputException()				
		elif keyval in (ibus.keysyms.minus, ibus.keysyms.bracketleft, ibus.keysyms.Page_Up):
			if self._editor.candidates:
				self._editor.lookup_table.page_up ();
				return True
			else:
				raise InputException()
		elif keyval==ibus.keysyms.Up:
			if self._editor.candidates:
				self._editor.lookup_table.cursor_up()
				self._editor.lookup_table.show_cursor(True)
				return True
			else:
				raise InputException()
		elif keyval==ibus.keysyms.Down:
			if self._editor.candidates:
				self._editor.lookup_table.cursor_down()
				self._editor.lookup_table.show_cursor(True)
				return True
			else:
				raise InputException()
		elif keyval == ibus.keysyms.Left or keyval == ibus.keysyms.b and state & ibus.modifier.CONTROL_MASK:
			self._editor.move_cursor (-1)
			return True
		elif keyval == ibus.keysyms.Right or keyval == ibus.keysyms.f and state & ibus.modifier.CONTROL_MASK:
			if self.get_extra_string():
				raise InputException()
			self._editor.move_cursor (1)
			return True
		elif keyval == ibus.keysyms.h and state & ibus.modifier.CONTROL_MASK or keyval == ibus.keysyms.Home:
			if self.get_extra_string():
				raise InputException()
			self._editor.move_cursor_to (1)
			return True
		elif keyval == ibus.keysyms.e and state & ibus.modifier.CONTROL_MASK or keyval == ibus.keysyms.End:
			if self.get_extra_string():
				raise InputException()
			self._editor.move_cursor_to (0)
			return True
		elif keyval in (ibus.keysyms.Return, ibus.keysyms.KP_Enter):
			self.commit_text (ibus.Text(self._editor.commit() + self.get_extra_string()))
			self.clear()
			return True
		elif keyval == ibus.keysyms.Escape or keyval == ibus.keysyms.c and state & ibus.modifier.CONTROL_MASK:
			if self.origin_string:
				self.commit_text(ibus.Text(self.origin_string))
				self._editor.clear()
				self.origin_string = None
			elif self._editor.lookup_table.is_cursor_visible():
				self._editor.lookup_table.show_cursor(False)
				self._editor.update()
			else:
				self.clear()
				self._editor.clear()
			return True
		elif keyval <= 127 and ascii.ispunct (chr (keyval)) and not self.get_extra_string():
			if not self._editor.is_empty ():
				self.commit_text (ibus.Text(self._editor.commit ()))
			self.commit_text (ibus.Text(self.convert_to_full_width (unichr (keyval))))
			return True
		else:
			raise InputException ()

	def convert_to_full_width (self, c):
		if c == u".":
			if self._prev_key and self._prev_key >= ibus.keysyms._0 and self._prev_key <= ibus.keysyms._9:
				return u"."
			else:
				return u"\u3002"
		elif c == u"\\":
			return u"\u3001"
		elif c == u"^":
			return u"\u2026\u2026"
		elif c == u"_":
			return u"\u2014\u2014"
		elif c == u"$":
			return u"\uffe5"
		elif c == u"\"":
			self._double_quotation_state = not self._double_quotation_state
			if self._double_quotation_state:
				return u"\u201c"
			else:
				return u"\u201d"
		elif c == u"'":
			self._single_quotation_state = not self._single_quotation_state
			if self._single_quotation_state:
				return u"\u2018"
			else:
				return u"\u2019"
		elif c == u"<":
			return u"\u300a"
		elif c == u">":
			return u"\u300b"
		return c
		#return scim.unichar_half_to_full (c)


def train(file_name):
	print "Training by " + file_name
	editor = Editor()
	import re
	ex = re.compile(ur"[\da-zA-Z\W]",re.UNICODE)
	for l in file(file_name):
		ll = unicode(l,"utf8")[:-1]
		t = ex.split(ll)
		for i in t:
			if i:
				try:
					editor.reverse(i)
					#~ print i
					editor.learn()
				except:
					print file
					traceback.print_exc ()
					raise Exception()				

def print_usage():
	print "ZhengJu -f FILE\tRead Sentenc from file"
	print "ZhengJu \tConvert parameter to pinyin"

if __name__ == "__main__":
	editor = Editor()
	import sys
	try:
		if len(sys.argv) == 3:
			if sys.argv[1] == "-f":
				train(sys.argv[2])
			else:
				raise Exception()
		elif len(sys.argv) == 2:
			try:
				editor.reverse(unicode(sys.argv[1],"utf8"))
				for i in editor.wordlist:
					print ID_PINYIN_DICT[i.get_pinyin_id()],
					print i.char,
			except:
				print "Can't convert this to pinyin"
				raise Exception()
		else:
			raise Exception()
	except:
		traceback.print_exc ()
		print_usage()
		sys.exit(1)
	else:
		sys.exit(0)

